<!DOCTYPE html>



  


<html class="theme-next muse use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT" />










<meta name="description" content="1、ConcurrentHashMap 与 HashTableHashTable 是 HashMap 的线程安全版本，使用的是 HashTable 的对象锁，同一时刻只能有一个线程 新增元素，获取元素。锁等待多，并发度低。而 ConcurrentHashMap 采用的是锁分段机制，就是用多把锁，让每把锁管理一部分数据。怎么实现的呢？引入了段(Segment)数据结构。 我们不妨来回忆一下HashM">
<meta property="og:type" content="article">
<meta property="og:title" content="java并发容器ConcurrentHashMap源码分析">
<meta property="og:url" content="https://www.codingw.net/posts/7310f98.html">
<meta property="og:site_name" content="中间件兴趣圈">
<meta property="og:description" content="1、ConcurrentHashMap 与 HashTableHashTable 是 HashMap 的线程安全版本，使用的是 HashTable 的对象锁，同一时刻只能有一个线程 新增元素，获取元素。锁等待多，并发度低。而 ConcurrentHashMap 采用的是锁分段机制，就是用多把锁，让每把锁管理一部分数据。怎么实现的呢？引入了段(Segment)数据结构。 我们不妨来回忆一下HashM">
<meta property="og:locale">
<meta property="og:image" content="https://img-blog.csdn.net/20161129121630568?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center">
<meta property="og:image" content="">
<meta property="og:image" content="https://img-blog.csdn.net/20161129121705356?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center">
<meta property="og:image" content="">
<meta property="article:published_time" content="2020-12-10T13:40:01.000Z">
<meta property="article:modified_time" content="2021-04-26T12:13:39.811Z">
<meta property="article:author" content="中间件兴趣圈">
<meta property="article:tag" content="中间件">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://img-blog.csdn.net/20161129121630568?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '',
    scheme: 'Muse',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://www.codingw.net/posts/7310f98.html"/>





  <title>java并发容器ConcurrentHashMap源码分析 | 中间件兴趣圈</title>
  








<meta name="generator" content="Hexo 5.4.0"></head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">中间件兴趣圈</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">微信搜『中间件兴趣圈』，回复『Java』获取200本优质电子书</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-question-circle"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-question-circle"></i> <br />
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://www.codingw.net/posts/7310f98.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="中间件兴趣圈">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">java并发容器ConcurrentHashMap源码分析</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-12-10T21:40:01+08:00">
                2020-12-10
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/juc/" itemprop="url" rel="index">
                    <span itemprop="name">juc</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          
             <span id="/posts/7310f98.html" class="leancloud_visitors" data-flag-title="java并发容器ConcurrentHashMap源码分析">
               <span class="post-meta-divider">|</span>
               <span class="post-meta-item-icon">
                 <i class="fa fa-eye"></i>
               </span>
               
                 <span class="post-meta-item-text">阅读次数&#58;</span>
               
                 <span class="leancloud-visitors-count"></span>
             </span>
          

          
            <span class="post-meta-divider">|</span>
            <span class="page-pv"><i class="fa fa-file-o"></i>
            <span class="busuanzi-value" id="busuanzi_value_page_pv" ></span>次
            </span>
          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <div id="vip-container"><h1 id="1、ConcurrentHashMap-与-HashTable"><a href="#1、ConcurrentHashMap-与-HashTable" class="headerlink" title="1、ConcurrentHashMap 与 HashTable"></a>1、ConcurrentHashMap 与 HashTable</h1><p>HashTable 是 HashMap 的线程安全版本，使用的是 HashTable 的对象锁，同一时刻只能有一个线程 新增元素，获取元素。锁等待多，并发度低。而 ConcurrentHashMap 采用的是锁分段机制，就是用多把锁，让每把锁管理一部分数据。怎么实现的呢？引入了段(Segment)数据结构。</p>
<p>我们不妨来回忆一下HashMap、HashTable 的数据结构</p>
<p><img src="https://img-blog.csdn.net/20161129121630568?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="img"><img src="" alt="点击并拖拽以移动"></p>
<p>结合下文对ConcurrentHashMap的分析，可以得知ConcurrentHashMap的数据结构如下，其实就是可以简单的认为，ConcurrentHashMap就是 HashMap[] 数组，就是一个数组，数组元素是一个一个的 HashMap。</p>
<p><img src="https://img-blog.csdn.net/20161129121705356?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="img"><span id="more"></span><img src="" alt="点击并拖拽以移动"></p>
<p>为了更好的理解ConcurrentHashMap，最好首先阅读如下几篇文章：</p>
<ul>
<li>HashMap源码分析 <a target="_blank" rel="noopener" href="http://blog.csdn.net/prestigeding/article/details/52861420">http://blog.csdn.net/prestigeding/article/details/52861420</a></li>
<li>使用Unsafe根据内存地址与偏移量访问数组元素方法：<a target="_blank" rel="noopener" href="http://blog.csdn.net/prestigeding/article/details/52980801">http://blog.csdn.net/prestigeding/article/details/52980801</a></li>
<li>ReentrantLock锁分析：<a target="_blank" rel="noopener" href="http://blog.csdn.net/prestigeding/article/details/53084883">http://blog.csdn.net/prestigeding/article/details/53084883</a></li>
</ul>
<h1 id="2、ConcurrentHashMap-详解"><a href="#2、ConcurrentHashMap-详解" class="headerlink" title="2、ConcurrentHashMap 详解"></a>2、ConcurrentHashMap 详解</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * A &#123;<span class="doctag">@link</span> java.util.Map&#125; providing additional atomic</span></span><br><span class="line"><span class="comment"> * &lt;tt&gt;putIfAbsent&lt;/tt&gt;, &lt;tt&gt;remove&lt;/tt&gt;, and &lt;tt&gt;replace&lt;/tt&gt; methods.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;Memory consistency effects: As with other concurrent</span></span><br><span class="line"><span class="comment"> * collections, actions in a thread prior to placing an object into a</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@code</span> ConcurrentMap&#125; as a key or value</span></span><br><span class="line"><span class="comment"> * &lt;a href=&quot;package-summary.html#MemoryVisibility&quot;&gt;&lt;i&gt;happen-before&lt;/i&gt;&lt;/a&gt;</span></span><br><span class="line"><span class="comment"> * actions subsequent to the access or removal of that object from</span></span><br><span class="line"><span class="comment"> * the &#123;<span class="doctag">@code</span> ConcurrentMap&#125; in another thread.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;This interface is a member of the</span></span><br><span class="line"><span class="comment"> * &lt;a href=&quot;&#123;<span class="doctag">@docRoot</span>&#125;/../technotes/guides/collections/index.html&quot;&gt;</span></span><br><span class="line"><span class="comment"> * Java Collections Framework&lt;/a&gt;.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@since</span> 1.5</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> Doug Lea</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;K&gt; the type of keys maintained by this map</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> &lt;V&gt; the type of mapped values</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ConcurrentMap</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; <span class="keyword">extends</span> <span class="title">Map</span>&lt;<span class="title">K</span>, <span class="title">V</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * If the specified key is not already associated</span></span><br><span class="line"><span class="comment">     * with a value, associate it with the given value.</span></span><br><span class="line"><span class="comment">     * This is equivalent to</span></span><br><span class="line"><span class="comment">     * &lt;pre&gt;</span></span><br><span class="line"><span class="comment">     *   if (!map.containsKey(key))</span></span><br><span class="line"><span class="comment">     *       return map.put(key, value);</span></span><br><span class="line"><span class="comment">     *   else</span></span><br><span class="line"><span class="comment">     *       return map.get(key);&lt;/pre&gt;</span></span><br><span class="line"><span class="comment">     * except that the action is performed atomically.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key key with which the specified value is to be associated</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value value to be associated with the specified key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> the previous value associated with the specified key, or</span></span><br><span class="line"><span class="comment">     *         &lt;tt&gt;null&lt;/tt&gt; if there was no mapping for the key.</span></span><br><span class="line"><span class="comment">     *         (A &lt;tt&gt;null&lt;/tt&gt; return can also indicate that the map</span></span><br><span class="line"><span class="comment">     *         previously associated &lt;tt&gt;null&lt;/tt&gt; with the key,</span></span><br><span class="line"><span class="comment">     *         if the implementation supports null values.)</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> UnsupportedOperationException if the &lt;tt&gt;put&lt;/tt&gt; operation</span></span><br><span class="line"><span class="comment">     *         is not supported by this map</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> ClassCastException if the class of the specified key or value</span></span><br><span class="line"><span class="comment">     *         prevents it from being stored in this map</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> NullPointerException if the specified key or value is null,</span></span><br><span class="line"><span class="comment">     *         and this map does not permit null keys or values</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> IllegalArgumentException if some property of the specified key</span></span><br><span class="line"><span class="comment">     *         or value prevents it from being stored in this map</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function">V <span class="title">putIfAbsent</span><span class="params">(K key, V value)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Removes the entry for a key only if currently mapped to a given value.</span></span><br><span class="line"><span class="comment">     * This is equivalent to</span></span><br><span class="line"><span class="comment">     * &lt;pre&gt;</span></span><br><span class="line"><span class="comment">     *   if (map.containsKey(key) &amp;&amp; map.get(key).equals(value)) &#123;</span></span><br><span class="line"><span class="comment">     *       map.remove(key);</span></span><br><span class="line"><span class="comment">     *       return true;</span></span><br><span class="line"><span class="comment">     *   &#125; else return false;&lt;/pre&gt;</span></span><br><span class="line"><span class="comment">     * except that the action is performed atomically.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key key with which the specified value is associated</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value value expected to be associated with the specified key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> &lt;tt&gt;true&lt;/tt&gt; if the value was removed</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> UnsupportedOperationException if the &lt;tt&gt;remove&lt;/tt&gt; operation</span></span><br><span class="line"><span class="comment">     *         is not supported by this map</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> ClassCastException if the key or value is of an inappropriate</span></span><br><span class="line"><span class="comment">     *         type for this map</span></span><br><span class="line"><span class="comment">     *         (&lt;a href=&quot;../Collection.html#optional-restrictions&quot;&gt;optional&lt;/a&gt;)</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> NullPointerException if the specified key or value is null,</span></span><br><span class="line"><span class="comment">     *         and this map does not permit null keys or values</span></span><br><span class="line"><span class="comment">     *         (&lt;a href=&quot;../Collection.html#optional-restrictions&quot;&gt;optional&lt;/a&gt;)</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">remove</span><span class="params">(Object key, Object value)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Replaces the entry for a key only if currently mapped to a given value.</span></span><br><span class="line"><span class="comment">     * This is equivalent to</span></span><br><span class="line"><span class="comment">     * &lt;pre&gt;</span></span><br><span class="line"><span class="comment">     *   if (map.containsKey(key) &amp;&amp; map.get(key).equals(oldValue)) &#123;</span></span><br><span class="line"><span class="comment">     *       map.put(key, newValue);</span></span><br><span class="line"><span class="comment">     *       return true;</span></span><br><span class="line"><span class="comment">     *   &#125; else return false;&lt;/pre&gt;</span></span><br><span class="line"><span class="comment">     * except that the action is performed atomically.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key key with which the specified value is associated</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> oldValue value expected to be associated with the specified key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> newValue value to be associated with the specified key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> &lt;tt&gt;true&lt;/tt&gt; if the value was replaced</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> UnsupportedOperationException if the &lt;tt&gt;put&lt;/tt&gt; operation</span></span><br><span class="line"><span class="comment">     *         is not supported by this map</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> ClassCastException if the class of a specified key or value</span></span><br><span class="line"><span class="comment">     *         prevents it from being stored in this map</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> NullPointerException if a specified key or value is null,</span></span><br><span class="line"><span class="comment">     *         and this map does not permit null keys or values</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> IllegalArgumentException if some property of a specified key</span></span><br><span class="line"><span class="comment">     *         or value prevents it from being stored in this map</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">replace</span><span class="params">(K key, V oldValue, V newValue)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Replaces the entry for a key only if currently mapped to some value.</span></span><br><span class="line"><span class="comment">     * This is equivalent to</span></span><br><span class="line"><span class="comment">     * &lt;pre&gt;</span></span><br><span class="line"><span class="comment">     *   if (map.containsKey(key)) &#123;</span></span><br><span class="line"><span class="comment">     *       return map.put(key, value);</span></span><br><span class="line"><span class="comment">     *   &#125; else return null;&lt;/pre&gt;</span></span><br><span class="line"><span class="comment">     * except that the action is performed atomically.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> key key with which the specified value is associated</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> value value to be associated with the specified key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> the previous value associated with the specified key, or</span></span><br><span class="line"><span class="comment">     *         &lt;tt&gt;null&lt;/tt&gt; if there was no mapping for the key.</span></span><br><span class="line"><span class="comment">     *         (A &lt;tt&gt;null&lt;/tt&gt; return can also indicate that the map</span></span><br><span class="line"><span class="comment">     *         previously associated &lt;tt&gt;null&lt;/tt&gt; with the key,</span></span><br><span class="line"><span class="comment">     *         if the implementation supports null values.)</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> UnsupportedOperationException if the &lt;tt&gt;put&lt;/tt&gt; operation</span></span><br><span class="line"><span class="comment">     *         is not supported by this map</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> ClassCastException if the class of the specified key or value</span></span><br><span class="line"><span class="comment">     *         prevents it from being stored in this map</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> NullPointerException if the specified key or value is null,</span></span><br><span class="line"><span class="comment">     *         and this map does not permit null keys or values</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> IllegalArgumentException if some property of the specified key</span></span><br><span class="line"><span class="comment">     *         or value prevents it from being stored in this map</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function">V <span class="title">replace</span><span class="params">(K key, V value)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="2-1-ConcurrentHashMap构造方法与数据结构分析"><a href="#2-1-ConcurrentHashMap构造方法与数据结构分析" class="headerlink" title="2.1 ConcurrentHashMap构造方法与数据结构分析"></a>2.1 ConcurrentHashMap构造方法与数据结构分析</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Creates a new, empty map with the specified initial</span></span><br><span class="line"><span class="comment">     * capacity, load factor and concurrency level.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> initialCapacity the initial capacity. The implementation</span></span><br><span class="line"><span class="comment">     * performs internal sizing to accommodate this many elements.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> loadFactor  the load factor threshold, used to control resizing.</span></span><br><span class="line"><span class="comment">     * Resizing may be performed when the average number of elements per</span></span><br><span class="line"><span class="comment">     * bin exceeds this threshold.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> concurrencyLevel the estimated number of concurrently</span></span><br><span class="line"><span class="comment">     * updating threads. The implementation performs internal sizing</span></span><br><span class="line"><span class="comment">     * to try to accommodate this many threads.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> IllegalArgumentException if the initial capacity is</span></span><br><span class="line"><span class="comment">     * negative or the load factor or concurrencyLevel are</span></span><br><span class="line"><span class="comment">     * nonpositive.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ConcurrentHashMap</span><span class="params">(<span class="keyword">int</span> initialCapacity,</span></span></span><br><span class="line"><span class="function"><span class="params">                             <span class="keyword">float</span> loadFactor, <span class="keyword">int</span> concurrencyLevel)</span> </span>&#123;                               <span class="comment">//@1</span></span><br><span class="line">        <span class="keyword">if</span> (!(loadFactor &gt; <span class="number">0</span>) || initialCapacity &lt; <span class="number">0</span> || concurrencyLevel &lt;= <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException();</span><br><span class="line">        <span class="keyword">if</span> (concurrencyLevel &gt; MAX_SEGMENTS)</span><br><span class="line">            concurrencyLevel = MAX_SEGMENTS;</span><br><span class="line">        <span class="comment">// Find power-of-two sizes best matching arguments</span></span><br><span class="line">        <span class="keyword">int</span> sshift = <span class="number">0</span>;                                                                                          <span class="comment">//@2 start</span></span><br><span class="line">        <span class="keyword">int</span> ssize = <span class="number">1</span>;   </span><br><span class="line">        <span class="keyword">while</span> (ssize &lt; concurrencyLevel) &#123;</span><br><span class="line">            ++sshift;</span><br><span class="line">            ssize &lt;&lt;= <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">this</span>.segmentShift = <span class="number">32</span> - sshift;</span><br><span class="line">        <span class="keyword">this</span>.segmentMask = ssize - <span class="number">1</span>;                                                              <span class="comment">//@2 end</span></span><br><span class="line">        <span class="keyword">if</span> (initialCapacity &gt; MAXIMUM_CAPACITY)</span><br><span class="line">            initialCapacity = MAXIMUM_CAPACITY;</span><br><span class="line">        <span class="keyword">int</span> c = initialCapacity / ssize;</span><br><span class="line">        <span class="keyword">if</span> (c * ssize &lt; initialCapacity)</span><br><span class="line">            ++c;</span><br><span class="line">        <span class="keyword">int</span> cap = MIN_SEGMENT_TABLE_CAPACITY; <span class="comment">// 每个 segment 内部容里</span></span><br><span class="line">        <span class="keyword">while</span> (cap &lt; c)</span><br><span class="line">            cap &lt;&lt;= <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// create segments and segments[0]</span></span><br><span class="line">        Segment&lt;K,V&gt; s0 =</span><br><span class="line">            <span class="keyword">new</span> Segment&lt;K,V&gt;(loadFactor, (<span class="keyword">int</span>)(cap * loadFactor),</span><br><span class="line">                             (HashEntry&lt;K,V&gt;[])<span class="keyword">new</span> HashEntry[cap]);</span><br><span class="line">        Segment&lt;K,V&gt;[] ss = (Segment&lt;K,V&gt;[])<span class="keyword">new</span> Segment[ssize];</span><br><span class="line">        UNSAFE.putOrderedObject(ss, SBASE, s0); <span class="comment">// ordered write of segments[0]</span></span><br><span class="line">        <span class="keyword">this</span>.segments = ss;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>代码@1 concurrencyLevel，含义，并发级别，并发度，在键不冲突的情况下，最多允许多少个线程同时访问数据不需要阻塞（理想情况下），我们应该知道，ConcurrentHashMap 的基本实现原理就是引入Segment 数据结构，将锁的粒度细化到Segment, 也就是说，如果多个线程，同时操作多个 key,如果这些 key，分布在不同的 Segment, 那这些线程的操作互不影响，当然不需要加锁，提高性能。所以 concurrencyLevel，就是要求告诉 ConcurrentHashMap, 我需要这么过个线程同时访问你而不产生锁冲突。</p>
<p>代码@2，ssize，该变量的值等于ConcurrentHashMap 中 segment 的长度，也就是 Segment[] 的长度。该值取决于concurrencyLevel, 其实就是小于concurrencyLevel 的最大的2的幂，比如concurrencyLevel= 16,那 ssize=16,如果 concurrencyLevel = 12, ssize=8，因为ssize的长度为2的幂。</p>
<p>变量shift的值，看出来了没，其实就是 ssize 2 ^ shift,其实就是表示ssize需要的二进制位。</p>
<p>segmentMask、segmentShift ，这两个属性在该表达式中使用：(h &gt;&gt;&gt; segmentShift) &amp; segmentMask)，很明显，就是用来算Segment[]数组中的下标来的。意图segmentShift = 32 - sshift，也就是利用hash的高位与代表（ssize-1）来定位下标。// 如果默认，初始容量16,那么ssize=16, sshift=4 定位端 hash 无符号向右移多少28位，（总共32位），那就是使原本32-29位参与运算（高位）</p>
<p>变量cap,就是每个Segment中HashEntity[]的长度，大于【初始容量/segment长度】的最小2的幂。</p>
<p>分析到这里，ConcurrentHashMap就构建成功了，我们先重点关注一下 Segment 的数据结构。</p>
<p>Segment 段的内部数据结构如下：</p>
<ul>
<li>类的声明：static final class Segment&lt;K,V&gt; extends ReentrantLock implements Serializable</li>
<li>数据结构：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">transient</span> <span class="keyword">volatile</span> HashEntry&lt;K,V&gt;[] table;    <span class="comment">// 内部键值对</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">transient</span> <span class="keyword">int</span> count;  <span class="comment">// 元素数量</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">transient</span> <span class="keyword">int</span> modCount;     <span class="comment">// 结构发生变化的次数</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">transient</span> <span class="keyword">int</span> threshold;    <span class="comment">// 扩容时的阔值</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">final</span> <span class="keyword">float</span> loadFactor;     <span class="comment">// 扩容因子，主要影响threshold，影响什么时候扩容</span></span><br></pre></td></tr></table></figure>

<p>对上述结构，是否似曾相识，对了，就是它，HashMap；每个 Segment 其实就是一个 HashMap; 还有一个很关键点：Segment继承自 ReentrantLock, 也就是 Segment 本身就是一把锁。</p>
<h2 id="2-2-public-V-put-K-key-V-value-源码分析"><a href="#2-2-public-V-put-K-key-V-value-源码分析" class="headerlink" title="2.2  public V put(K key, V value) 源码分析"></a>2.2  public V put(K key, V value) 源码分析</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">        Segment&lt;K,V&gt; s;</span><br><span class="line">        <span class="keyword">if</span> (value == <span class="keyword">null</span>)                                      </span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();   <span class="comment">// @1</span></span><br><span class="line">        <span class="keyword">int</span> hash = hash(key);</span><br><span class="line">        <span class="keyword">int</span> j = (hash &gt;&gt;&gt; segmentShift) &amp; segmentMask;   <span class="comment">//@2</span></span><br><span class="line">        <span class="keyword">if</span> ((s = (Segment&lt;K,V&gt;)UNSAFE.getObject          <span class="comment">// nonvolatile; recheck in ensureSegment</span></span><br><span class="line">             (segments, (j &lt;&lt; SSHIFT) + SBASE)) == <span class="keyword">null</span>) <span class="comment">//  in ensureSegment  @3</span></span><br><span class="line">            s = ensureSegment(j);                                      <span class="comment">//@4</span></span><br><span class="line">        <span class="keyword">return</span> s.put(key, hash, value, <span class="keyword">false</span>);                   <span class="comment">//@5</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>代码@1，表明 ConcurrentHashMap不支持value为空的键值对。</p>
<p>代码@2，计算该key对应的Segment的位置（数组下标），并发包中获取数组元素的方式，采用的是UNSAFE直接操作内存的方式，而不是典型的  Segment[] a = new Segment[16],  第j个元素的值为  a[j]。如果需要详细了解UNSAFE操作数组元素的原理，请查看  另一篇博客(AtomicIntegerArray 源码分析)</p>
<p>比如一个Integer[]中，每个int是32位，占4个字节，那数组中第3个位置的开始字节是多少呢？=(3-1) &lt;&lt; 2,也就是说SHIFT的值为元素中长度的幂。怎么获取每个元素在数组中长度（字节为单位）= UNSAFE.arrayIndexScale,</p>
<p>而 UNSAFE.arrayBaseOffset,返回的是，第一个数据元素相对于对象起始地址的便宜量，该部分的详解，请参考我的技术博客【<a target="_blank" rel="noopener" href="http://blog.csdn.net/prestigeding/article/details/52980801%E3%80%91">http://blog.csdn.net/prestigeding/article/details/52980801】</a></p>
<p>代码@3，就是获取j下标的segment对象。相当于  if(  (s == segments[j])== null  )</p>
<p>代码@4，我们将目光移到 ensureSegment方法中：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns the segment for the given index, creating it and</span></span><br><span class="line"><span class="comment">     * recording in segment table (via CAS) if not already present.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> k the index</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> the segment</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> Segment&lt;K,V&gt; <span class="title">ensureSegment</span><span class="params">(<span class="keyword">int</span> k)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> Segment&lt;K,V&gt;[] ss = <span class="keyword">this</span>.segments;</span><br><span class="line">        <span class="keyword">long</span> u = (k &lt;&lt; SSHIFT) + SBASE; <span class="comment">// raw offset</span></span><br><span class="line">        Segment&lt;K,V&gt; seg;</span><br><span class="line">        <span class="keyword">if</span> ((seg = (Segment&lt;K,V&gt;)UNSAFE.getObjectVolatile(ss, u)) == <span class="keyword">null</span>) &#123;</span><br><span class="line">            Segment&lt;K,V&gt; proto = ss[<span class="number">0</span>]; <span class="comment">// use segment 0 as prototype</span></span><br><span class="line">            <span class="keyword">int</span> cap = proto.table.length;</span><br><span class="line">            <span class="keyword">float</span> lf = proto.loadFactor;</span><br><span class="line">            <span class="keyword">int</span> threshold = (<span class="keyword">int</span>)(cap * lf);</span><br><span class="line">            HashEntry&lt;K,V&gt;[] tab = (HashEntry&lt;K,V&gt;[])<span class="keyword">new</span> HashEntry[cap];</span><br><span class="line">            <span class="keyword">if</span> ((seg = (Segment&lt;K,V&gt;)UNSAFE.getObjectVolatile(ss, u))</span><br><span class="line">                == <span class="keyword">null</span>) &#123; <span class="comment">// recheck</span></span><br><span class="line">                Segment&lt;K,V&gt; s = <span class="keyword">new</span> Segment&lt;K,V&gt;(lf, threshold, tab);</span><br><span class="line">                <span class="keyword">while</span> ((seg = (Segment&lt;K,V&gt;)UNSAFE.getObjectVolatile(ss, u))</span><br><span class="line">                       == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (UNSAFE.compareAndSwapObject(ss, u, <span class="keyword">null</span>, seg = s))</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> seg;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>该方法，主要是确保segment槽的k位置的Segment不为空，如果为空，初始化。</p>
<p>代码@5，代码@4初始化k位置的segment后，将键值对加入到segment,接下重点看一下Segment的put方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> V <span class="title">put</span><span class="params">(K key, <span class="keyword">int</span> hash, V value, <span class="keyword">boolean</span> onlyIfAbsent)</span> </span>&#123;</span><br><span class="line">            HashEntry&lt;K,V&gt; node = tryLock() ? <span class="keyword">null</span> :</span><br><span class="line">                scanAndLockForPut(key, hash, value);   <span class="comment">// @1</span></span><br><span class="line">            V oldValue;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                HashEntry&lt;K,V&gt;[] tab = table;</span><br><span class="line">                <span class="keyword">int</span> index = (tab.length - <span class="number">1</span>) &amp; hash;</span><br><span class="line">                HashEntry&lt;K,V&gt; first = entryAt(tab, index);      <span class="comment">// @2</span></span><br><span class="line">                <span class="keyword">for</span> (HashEntry&lt;K,V&gt; e = first;;) &#123;                         <span class="comment">// @3</span></span><br><span class="line">                    <span class="keyword">if</span> (e != <span class="keyword">null</span>) &#123;                                                     <span class="comment">// @4           </span></span><br><span class="line">                        K k;</span><br><span class="line">                        <span class="keyword">if</span> ((k = e.key) == key ||                                          </span><br><span class="line">                            (e.hash == hash &amp;&amp; key.equals(k))) &#123;      <span class="comment">//@5</span></span><br><span class="line">                            oldValue = e.value;</span><br><span class="line">                            <span class="keyword">if</span> (!onlyIfAbsent) &#123;</span><br><span class="line">                                e.value = value;</span><br><span class="line">                                ++modCount;</span><br><span class="line">                            &#125;</span><br><span class="line">                            <span class="keyword">break</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">                        e = e.next;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">else</span> &#123;                                                                   <span class="comment">//@6                                                        </span></span><br><span class="line">                        <span class="keyword">if</span> (node != <span class="keyword">null</span>)</span><br><span class="line">                            node.setNext(first);</span><br><span class="line">                        <span class="keyword">else</span></span><br><span class="line">                            node = <span class="keyword">new</span> HashEntry&lt;K,V&gt;(hash, key, value, first);</span><br><span class="line">                        <span class="keyword">int</span> c = count + <span class="number">1</span>;</span><br><span class="line">                        <span class="keyword">if</span> (c &gt; threshold &amp;&amp; tab.length &lt; MAXIMUM_CAPACITY)</span><br><span class="line">                            rehash(node);</span><br><span class="line">                        <span class="keyword">else</span></span><br><span class="line">                            setEntryAt(tab, index, node);</span><br><span class="line">                        ++modCount;</span><br><span class="line">                        count = c;</span><br><span class="line">                        oldValue = <span class="keyword">null</span>;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                unlock();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> oldValue;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>

<p>该方法，实现思路其实和HashMap一样，就是要在Segment的HashEntity[] table的指定位置加入新的Node,如果在位置k的位置不为空，此时，说明该位置发生了hash冲突，这是需要先遍历整个链，看是否有相等的key,如果key相等，则替换该值，如果没有，则将新加入的节点的next指针指向 table[k],然后将node加入到k位置。但是，由于ConcurrentHashMap是支持多个线程同时访问的，对于单个Segment的操作，需要加锁。</p>
<p>代码@1，首先尝试获取锁，如果成功获取锁，则继续添加元素，如果获取锁失败，后面重点分析。</p>
<p>代码@2，获取该key所对应的table[]中的下标。根据该元素是否为空，有两种操作，如果为空，说明没有发生冲突，也就是走代码@6分支，就是将新创建的节点的节点放入table[k]处，当然，此时需要判断是否需要进行rehash操作（ConcurrentHashMap的是否需要rehash,就是判断阔值）。</p>
<p>代码@4，就是循环判断table[k]的链条中，是否有key与待操作key相等，如果相等，直接替换就好。由于@3开始，其实就是整个put方法，会在锁保护中。</p>
<p>上述过程，应该很好理解，所以，现在重点关注两个方法，一是scanAndLockForPut，二是rehash(比较好奇，是否与HashMap相同，应该是一样的吧，呵呵)。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Scans for a node containing given key while trying to</span></span><br><span class="line"><span class="comment">         * acquire lock, creating and returning one if not found. Upon</span></span><br><span class="line"><span class="comment">         * return, guarantees that lock is held. UNlike in most</span></span><br><span class="line"><span class="comment">         * methods, calls to method equals are not screened: Since</span></span><br><span class="line"><span class="comment">         * traversal speed doesn&#x27;t matter, we might as well help warm</span></span><br><span class="line"><span class="comment">         * up the associated code and accesses as well.</span></span><br><span class="line"><span class="comment">         *</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@return</span> a new node if key not found, else null</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">private</span> HashEntry&lt;K,V&gt; <span class="title">scanAndLockForPut</span><span class="params">(K key, <span class="keyword">int</span> hash, V value)</span> </span>&#123;</span><br><span class="line">            HashEntry&lt;K,V&gt; first = entryForHash(<span class="keyword">this</span>, hash);</span><br><span class="line">            HashEntry&lt;K,V&gt; e = first;</span><br><span class="line">            HashEntry&lt;K,V&gt; node = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">int</span> retries = -<span class="number">1</span>; <span class="comment">// negative while locating node</span></span><br><span class="line">            <span class="keyword">while</span> (!tryLock()) &#123;</span><br><span class="line">                HashEntry&lt;K,V&gt; f; <span class="comment">// to recheck first below</span></span><br><span class="line">                <span class="keyword">if</span> (retries &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (e == <span class="keyword">null</span>) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (node == <span class="keyword">null</span>) <span class="comment">// speculatively create node</span></span><br><span class="line">                            node = <span class="keyword">new</span> HashEntry&lt;K,V&gt;(hash, key, value, <span class="keyword">null</span>);</span><br><span class="line">                        retries = <span class="number">0</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">else</span> <span class="keyword">if</span> (key.equals(e.key))</span><br><span class="line">                        retries = <span class="number">0</span>;</span><br><span class="line">                    <span class="keyword">else</span></span><br><span class="line">                        e = e.next;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (++retries &gt; MAX_SCAN_RETRIES) &#123;</span><br><span class="line">                    lock();</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> ((retries &amp; <span class="number">1</span>) == <span class="number">0</span> &amp;&amp;</span><br><span class="line">                         (f = entryForHash(<span class="keyword">this</span>, hash)) != first) &#123;</span><br><span class="line">                    e = first = f; <span class="comment">// re-traverse if entry changed</span></span><br><span class="line">                    retries = -<span class="number">1</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> node;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>

<p>在没有成功获取锁的情况下，先不急于阻塞，而是乐观的估计获取锁的线程操作的key与当前操作的key没关系，那我该干嘛就干嘛，自旋尝试获取锁（尝试MAX_SCAN_RETRIES，如果未成功获取锁）尝试超过最大尝试次数，为了性能考虑，该线程阻塞，参加代码@2。</p>
<p>@3，每隔一次，检查一下 Segment HashEntity[] table 处k的位置的元素是否发生变化，如果发生变化，则重试次数设置为-1，继续尝试获取锁。该方法如果在阻塞在lock()方法，时，一旦获取锁，则进入到final V put(K key, int hash, V value, boolean onlyIfAbsent) 方法中，进行常规的put方法（与HashMap操作类似。）</p>
<p>接下来重点看一下代码@7，如果当前segment中容量大于阔值，并小于允许的最大长度时，需要进行rehash,下面分析一下rehash源码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Doubles size of table and repacks entries, also adding the</span></span><br><span class="line"><span class="comment">         * given node to new table</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">rehash</span><span class="params">(HashEntry&lt;K,V&gt; node)</span> </span>&#123;</span><br><span class="line">            <span class="comment">/*</span></span><br><span class="line"><span class="comment">             * Reclassify nodes in each list to new table.  Because we</span></span><br><span class="line"><span class="comment">             * are using power-of-two expansion, the elements from</span></span><br><span class="line"><span class="comment">             * each bin must either stay at same index, or move with a</span></span><br><span class="line"><span class="comment">             * power of two offset. We eliminate unnecessary node</span></span><br><span class="line"><span class="comment">             * creation by catching cases where old nodes can be</span></span><br><span class="line"><span class="comment">             * reused because their next fields won&#x27;t change.</span></span><br><span class="line"><span class="comment">             * Statistically, at the default threshold, only about</span></span><br><span class="line"><span class="comment">             * one-sixth of them need cloning when a table</span></span><br><span class="line"><span class="comment">             * doubles. The nodes they replace will be garbage</span></span><br><span class="line"><span class="comment">             * collectable as soon as they are no longer referenced by</span></span><br><span class="line"><span class="comment">             * any reader thread that may be in the midst of</span></span><br><span class="line"><span class="comment">             * concurrently traversing table. Entry accesses use plain</span></span><br><span class="line"><span class="comment">             * array indexing because they are followed by volatile</span></span><br><span class="line"><span class="comment">             * table write.</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            HashEntry&lt;K,V&gt;[] oldTable = table;</span><br><span class="line">            <span class="keyword">int</span> oldCapacity = oldTable.length;</span><br><span class="line">            <span class="keyword">int</span> newCapacity = oldCapacity &lt;&lt; <span class="number">1</span>;</span><br><span class="line">            threshold = (<span class="keyword">int</span>)(newCapacity * loadFactor);</span><br><span class="line">            HashEntry&lt;K,V&gt;[] newTable =</span><br><span class="line">                (HashEntry&lt;K,V&gt;[]) <span class="keyword">new</span> HashEntry[newCapacity];</span><br><span class="line">            <span class="keyword">int</span> sizeMask = newCapacity - <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; oldCapacity ; i++) &#123;</span><br><span class="line">                HashEntry&lt;K,V&gt; e = oldTable[i];</span><br><span class="line">                <span class="keyword">if</span> (e != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    HashEntry&lt;K,V&gt; next = e.next;</span><br><span class="line">                    <span class="keyword">int</span> idx = e.hash &amp; sizeMask;</span><br><span class="line">                    <span class="keyword">if</span> (next == <span class="keyword">null</span>)   <span class="comment">//  Single node on list</span></span><br><span class="line">                        newTable[idx] = e;</span><br><span class="line">                    <span class="keyword">else</span> &#123; <span class="comment">// Reuse consecutive sequence at same slot</span></span><br><span class="line">                        HashEntry&lt;K,V&gt; lastRun = e;</span><br><span class="line">                        <span class="keyword">int</span> lastIdx = idx;</span><br><span class="line">                        <span class="keyword">for</span> (HashEntry&lt;K,V&gt; last = next;</span><br><span class="line">                             last != <span class="keyword">null</span>;</span><br><span class="line">                             last = last.next) &#123;</span><br><span class="line">                            <span class="keyword">int</span> k = last.hash &amp; sizeMask;</span><br><span class="line">                            <span class="keyword">if</span> (k != lastIdx) &#123;</span><br><span class="line">                                lastIdx = k;</span><br><span class="line">                                lastRun = last;</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        newTable[lastIdx] = lastRun;</span><br><span class="line">                        <span class="comment">// Clone remaining nodes</span></span><br><span class="line">                        <span class="keyword">for</span> (HashEntry&lt;K,V&gt; p = e; p != lastRun; p = p.next) &#123;</span><br><span class="line">                            V v = p.value;</span><br><span class="line">                            <span class="keyword">int</span> h = p.hash;</span><br><span class="line">                            <span class="keyword">int</span> k = h &amp; sizeMask;</span><br><span class="line">                            HashEntry&lt;K,V&gt; n = newTable[k];</span><br><span class="line">                            newTable[k] = <span class="keyword">new</span> HashEntry&lt;K,V&gt;(h, p.key, v, n);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">int</span> nodeIndex = node.hash &amp; sizeMask; <span class="comment">// add the new node</span></span><br><span class="line">            node.setNext(newTable[nodeIndex]);</span><br><span class="line">            newTable[nodeIndex] = node;</span><br><span class="line">            table = newTable;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>

<p>在理解了HashMap的rehash方法后，再来看该方法，应该能很好的理解，故不做重复讲解了。</p>
<h3 id="2-2-2-public-V-putIfAbsent-K-key-V-value"><a href="#2-2-2-public-V-putIfAbsent-K-key-V-value" class="headerlink" title="2.2.2 public V putIfAbsent(K key, V value)"></a>2.2.2 public V putIfAbsent(K key, V value)</h3><p>该方法的语义是，如果存在key，则直接返回key关联的value,如果key不存在，则加入该键值对，并返回null；该步骤是原子操作。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">putIfAbsent</span><span class="params">(K key, V value)</span> </span>&#123;</span><br><span class="line">        Segment&lt;K,V&gt; s;</span><br><span class="line">        <span class="keyword">if</span> (value == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">        <span class="keyword">int</span> hash = hash(key);</span><br><span class="line">        <span class="keyword">int</span> j = (hash &gt;&gt;&gt; segmentShift) &amp; segmentMask;</span><br><span class="line">        <span class="keyword">if</span> ((s = (Segment&lt;K,V&gt;)UNSAFE.getObject</span><br><span class="line">             (segments, (j &lt;&lt; SSHIFT) + SBASE)) == <span class="keyword">null</span>)</span><br><span class="line">            s = ensureSegment(j);</span><br><span class="line">        <span class="keyword">return</span> s.put(key, hash, value, <span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>该方法与put方法的实现基本相同，唯一不同的是，对已经存在key时，put方法是直接覆盖旧值，而putIfAbsent是，返回旧值。</p>
<h3 id="2-2-3-public-void-putAll-Map-m"><a href="#2-2-3-public-void-putAll-Map-m" class="headerlink" title="2.2.3 public void putAll(Map m)"></a>2.2.3 public void putAll(Map m)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">putAll</span><span class="params">(Map&lt;? extends K, ? extends V&gt; m)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (Map.Entry&lt;? extends K, ? extends V&gt; e : m.entrySet())</span><br><span class="line">            put(e.getKey(), e.getValue());</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>直接将传人的Map类型的参数，遍历，调用put方法。</p>
<p>看过了put函数，我们将目标转向到get方法中，瞧一瞧get相关方法的实现：</p>
<h3 id="2-2-4-public-V-get-Object-key-源码分析"><a href="#2-2-4-public-V-get-Object-key-源码分析" class="headerlink" title="2.2.4 public V get(Object key)源码分析"></a>2.2.4 public V get(Object key)源码分析</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns the value to which the specified key is mapped,</span></span><br><span class="line"><span class="comment">     * or &#123;<span class="doctag">@code</span> null&#125; if this map contains no mapping for the key.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * &lt;p&gt;More formally, if this map contains a mapping from a key</span></span><br><span class="line"><span class="comment">     * &#123;<span class="doctag">@code</span> k&#125; to a value &#123;<span class="doctag">@code</span> v&#125; such that &#123;<span class="doctag">@code</span> key.equals(k)&#125;,</span></span><br><span class="line"><span class="comment">     * then this method returns &#123;<span class="doctag">@code</span> v&#125;; otherwise it returns</span></span><br><span class="line"><span class="comment">     * &#123;<span class="doctag">@code</span> null&#125;.  (There can be at most one such mapping.)</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> NullPointerException if the specified key is null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">        Segment&lt;K,V&gt; s; <span class="comment">// manually integrate access methods to reduce overhead</span></span><br><span class="line">        HashEntry&lt;K,V&gt;[] tab;</span><br><span class="line">        <span class="keyword">int</span> h = hash(key);</span><br><span class="line">        <span class="keyword">long</span> u = (((h &gt;&gt;&gt; segmentShift) &amp; segmentMask) &lt;&lt; SSHIFT) + SBASE;</span><br><span class="line">        <span class="keyword">if</span> ((s = (Segment&lt;K,V&gt;)UNSAFE.getObjectVolatile(segments, u)) != <span class="keyword">null</span> &amp;&amp;</span><br><span class="line">            (tab = s.table) != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (HashEntry&lt;K,V&gt; e = (HashEntry&lt;K,V&gt;) UNSAFE.getObjectVolatile</span><br><span class="line">                     (tab, ((<span class="keyword">long</span>)(((tab.length - <span class="number">1</span>) &amp; h)) &lt;&lt; TSHIFT) + TBASE);</span><br><span class="line">                 e != <span class="keyword">null</span>; e = e.next) &#123;</span><br><span class="line">                K k;</span><br><span class="line">                <span class="keyword">if</span> ((k = e.key) == key || (e.hash == h &amp;&amp; key.equals(k)))</span><br><span class="line">                    <span class="keyword">return</span> e.value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>从上文中可以看到，get方法并没有加锁，只是根据key的hash，然后算出Segment槽的位置，不是直接根据下标去获取Segment，也不是直接根据下标去Segment 的 HashEntity[] tab中去获取元素，而是使用了 UNSAFE.getObjectVolatile方法，直接操作内存，并使用volatile方式获取，最大程度保证可见性。有人或许有疑问，为什么get方法不加读锁，阻止其他写入请求呢？其实这样做意义并不大，ConcurrentHashMap的是一个容器，数据存储，提供基本的 put,get操作，对单一一个get请求加锁，没什么意义，因为get方法并不会改变ConcurrentHashMap的内部结构，在当前线程获取到key中的值，然后其他线程删除了该key,这在业务场景上本身就是正常不过的操作。所以get方法并不需要加锁。</p>
<p>2.3 浏览源码，发现无论是replace方法，还是remove方法等操作内部等都和HashMap相似，因为Segment就是一个带锁的HashMap。所以，接下来，我们可以这样思考，put,replace,remove这些方法比HashMap效率高，因为提供了并发度，那这些获取全局的属性的方法呢，比如keys,size等这些方法，性能又是如何呢？我们将目光转向size，keys等遍历方法。</p>
<h3 id="2-3-1-public-int-size方法"><a href="#2-3-1-public-int-size方法" class="headerlink" title="2.3.1 public int size方法"></a>2.3.1 public int size方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns the number of key-value mappings in this map.  If the</span></span><br><span class="line"><span class="comment">     * map contains more than &lt;tt&gt;Integer.MAX_VALUE&lt;/tt&gt; elements, returns</span></span><br><span class="line"><span class="comment">     * &lt;tt&gt;Integer.MAX_VALUE&lt;/tt&gt;.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> the number of key-value mappings in this map</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// Try a few times to get accurate count. On failure due to</span></span><br><span class="line">        <span class="comment">// continuous async changes in table, resort to locking.</span></span><br><span class="line">        <span class="keyword">final</span> Segment&lt;K,V&gt;[] segments = <span class="keyword">this</span>.segments;</span><br><span class="line">        <span class="keyword">int</span> size;</span><br><span class="line">        <span class="keyword">boolean</span> overflow; <span class="comment">// true if size overflows 32 bits</span></span><br><span class="line">        <span class="keyword">long</span> sum;         <span class="comment">// sum of modCounts</span></span><br><span class="line">        <span class="keyword">long</span> last = <span class="number">0L</span>;   <span class="comment">// previous sum</span></span><br><span class="line">        <span class="keyword">int</span> retries = -<span class="number">1</span>; <span class="comment">// first iteration isn&#x27;t retry</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">                <span class="keyword">if</span> (retries++ == RETRIES_BEFORE_LOCK) &#123;</span><br><span class="line">                    <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; segments.length; ++j)</span><br><span class="line">                        ensureSegment(j).lock(); <span class="comment">// force creation</span></span><br><span class="line">                &#125;</span><br><span class="line">                sum = <span class="number">0L</span>;</span><br><span class="line">                size = <span class="number">0</span>;</span><br><span class="line">                overflow = <span class="keyword">false</span>;</span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; segments.length; ++j) &#123;</span><br><span class="line">                    Segment&lt;K,V&gt; seg = segmentAt(segments, j);</span><br><span class="line">                    <span class="keyword">if</span> (seg != <span class="keyword">null</span>) &#123;</span><br><span class="line">                        sum += seg.modCount;</span><br><span class="line">                        <span class="keyword">int</span> c = seg.count;</span><br><span class="line">                        <span class="keyword">if</span> (c &lt; <span class="number">0</span> || (size += c) &lt; <span class="number">0</span>)</span><br><span class="line">                            overflow = <span class="keyword">true</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (sum == last)</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                last = sum;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (retries &gt; RETRIES_BEFORE_LOCK) &#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; segments.length; ++j)</span><br><span class="line">                    segmentAt(segments, j).unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> overflow ? Integer.MAX_VALUE : size;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>该方法的核心实现原理：从上文的解读，我想大家应该已经了解每一个Segment就是一个HashMap,HashMap中有两个变量，modCount，表示数据结构发生变化次数，比如put一个未在HashMap中包含的key,比如remove,比如clear方法，每调用一次上述方法，modCount就加1，也就是影响size属性的操作，都会将modeCount加一；另一个变量size，记录HashMap中键值对的个数。那ConcurrentHashMap的size方法，如果结构没有发生改变，只需将各个Segment的size相加，就可以得到ConcurrentHashMap的size,然并卯，在相加的过程其他线程如果有改变Segment内部的结构的话，导致size不准确，该方法的实现办法，是先乐观的尝试计算相加的过程最多三次，最少两次，如果前后两次的modCount一样，就说明在计算size的过程中，其他线程并没有改变ConcurrentHashMap的结构没有变化，则可以直接将size返回，结束该方法的调用，如果有变化，则需要依次对所有Segment申请加锁操作，只有获取全部锁后，然后对每个segment的size相加，然后是否锁，并返回size值。</p>
<p>代码@1,如果重试次数达到 (RETRIES_BEFORE_LOCK +１　,默认为2)次数后，说明需要加锁才能计算。</p>
<p>代码@2，对Segment相加计算size</p>
<p>代码@3，就是实现，判断连续两次计算出的modCount相等，说明该size值正确，否则，继续尝试，获取去请求锁。</p>
<h3 id="2-3-2-public-boolean-isEmpty-方法源码解读"><a href="#2-3-2-public-boolean-isEmpty-方法源码解读" class="headerlink" title="2.3.2 public boolean isEmpty() 方法源码解读"></a>2.3.2 public boolean isEmpty() 方法源码解读</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns &lt;tt&gt;true&lt;/tt&gt; if this map contains no key-value mappings.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> &lt;tt&gt;true&lt;/tt&gt; if this map contains no key-value mappings</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * Sum per-segment modCounts to avoid mis-reporting when</span></span><br><span class="line"><span class="comment">         * elements are concurrently added and removed in one segment</span></span><br><span class="line"><span class="comment">         * while checking another, in which case the table was never</span></span><br><span class="line"><span class="comment">         * actually empty at any point. (The sum ensures accuracy up</span></span><br><span class="line"><span class="comment">         * through at least 1&lt;&lt;31 per-segment modifications before</span></span><br><span class="line"><span class="comment">         * recheck.)  Methods size() and containsValue() use similar</span></span><br><span class="line"><span class="comment">         * constructions for stability checks.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">long</span> sum = <span class="number">0L</span>;</span><br><span class="line">        <span class="keyword">final</span> Segment&lt;K,V&gt;[] segments = <span class="keyword">this</span>.segments;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; segments.length; ++j) &#123;</span><br><span class="line">            Segment&lt;K,V&gt; seg = segmentAt(segments, j);</span><br><span class="line">            <span class="keyword">if</span> (seg != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (seg.count != <span class="number">0</span>)</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">                sum += seg.modCount;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (sum != <span class="number">0L</span>) &#123; <span class="comment">// recheck unless no modifications</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; segments.length; ++j) &#123;</span><br><span class="line">                Segment&lt;K,V&gt; seg = segmentAt(segments, j);</span><br><span class="line">                <span class="keyword">if</span> (seg != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (seg.count != <span class="number">0</span>)</span><br><span class="line">                        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">                    sum -= seg.modCount;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (sum != <span class="number">0L</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>该方法的核心实现原理：就是遍历有所有的segment，一旦发现有存在size不等于0的segment，则返回false；如果发现所有的segment的size为0，则再次遍历，如果两次遍历时 modCount一样，则返回true,否则返回false。</p>
<p>大家再看看如下方法：</p>
<h3 id="2-3-3-public-boolean-containsKey-Object-key"><a href="#2-3-3-public-boolean-containsKey-Object-key" class="headerlink" title="2.3.3 public boolean containsKey(Object key)"></a>2.3.3 public boolean containsKey(Object key)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Tests if the specified object is a key in this table.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span>  key   possible key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> &lt;tt&gt;true&lt;/tt&gt; if and only if the specified object</span></span><br><span class="line"><span class="comment">     *         is a key in this table, as determined by the</span></span><br><span class="line"><span class="comment">     *         &lt;tt&gt;equals&lt;/tt&gt; method; &lt;tt&gt;false&lt;/tt&gt; otherwise.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> NullPointerException if the specified key is null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">containsKey</span><span class="params">(Object key)</span> </span>&#123;</span><br><span class="line">        Segment&lt;K,V&gt; s; <span class="comment">// same as get() except no need for volatile value read</span></span><br><span class="line">        HashEntry&lt;K,V&gt;[] tab;</span><br><span class="line">        <span class="keyword">int</span> h = hash(key);</span><br><span class="line">        <span class="keyword">long</span> u = (((h &gt;&gt;&gt; segmentShift) &amp; segmentMask) &lt;&lt; SSHIFT) + SBASE;</span><br><span class="line">        <span class="keyword">if</span> ((s = (Segment&lt;K,V&gt;)UNSAFE.getObjectVolatile(segments, u)) != <span class="keyword">null</span> &amp;&amp;</span><br><span class="line">            (tab = s.table) != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (HashEntry&lt;K,V&gt; e = (HashEntry&lt;K,V&gt;) UNSAFE.getObjectVolatile</span><br><span class="line">                     (tab, ((<span class="keyword">long</span>)(((tab.length - <span class="number">1</span>) &amp; h)) &lt;&lt; TSHIFT) + TBASE);</span><br><span class="line">                 e != <span class="keyword">null</span>; e = e.next) &#123;</span><br><span class="line">                K k;</span><br><span class="line">                <span class="keyword">if</span> ((k = e.key) == key || (e.hash == h &amp;&amp; key.equals(k)))</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<h3 id="2-3-4-public-boolean-containsValue-Object-value"><a href="#2-3-4-public-boolean-containsValue-Object-value" class="headerlink" title="2.3.4 public boolean containsValue(Object value)"></a>2.3.4 public boolean containsValue(Object value)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">   * Returns &lt;tt&gt;true&lt;/tt&gt; if this map maps one or more keys to the</span></span><br><span class="line"><span class="comment">   * specified value. Note: This method requires a full internal</span></span><br><span class="line"><span class="comment">   * traversal of the hash table, and so is much slower than</span></span><br><span class="line"><span class="comment">   * method &lt;tt&gt;containsKey&lt;/tt&gt;.</span></span><br><span class="line"><span class="comment">   *</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@param</span> value value whose presence in this map is to be tested</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@return</span> &lt;tt&gt;true&lt;/tt&gt; if this map maps one or more keys to the</span></span><br><span class="line"><span class="comment">   *         specified value</span></span><br><span class="line"><span class="comment">   * <span class="doctag">@throws</span> NullPointerException if the specified value is null</span></span><br><span class="line"><span class="comment">   */</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">containsValue</span><span class="params">(Object value)</span> </span>&#123;</span><br><span class="line">      <span class="comment">// Same idea as size()</span></span><br><span class="line">      <span class="keyword">if</span> (value == <span class="keyword">null</span>)</span><br><span class="line">          <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">      <span class="keyword">final</span> Segment&lt;K,V&gt;[] segments = <span class="keyword">this</span>.segments;</span><br><span class="line">      <span class="keyword">boolean</span> found = <span class="keyword">false</span>;</span><br><span class="line">      <span class="keyword">long</span> last = <span class="number">0</span>;</span><br><span class="line">      <span class="keyword">int</span> retries = -<span class="number">1</span>;</span><br><span class="line">      <span class="keyword">try</span> &#123;</span><br><span class="line">          outer: <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">              <span class="keyword">if</span> (retries++ == RETRIES_BEFORE_LOCK) &#123;</span><br><span class="line">                  <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; segments.length; ++j)</span><br><span class="line">                      ensureSegment(j).lock(); <span class="comment">// force creation</span></span><br><span class="line">              &#125;</span><br><span class="line">              <span class="keyword">long</span> hashSum = <span class="number">0L</span>;</span><br><span class="line">              <span class="keyword">int</span> sum = <span class="number">0</span>;</span><br><span class="line">              <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; segments.length; ++j) &#123;</span><br><span class="line">                  HashEntry&lt;K,V&gt;[] tab;</span><br><span class="line">                  Segment&lt;K,V&gt; seg = segmentAt(segments, j);</span><br><span class="line">                  <span class="keyword">if</span> (seg != <span class="keyword">null</span> &amp;&amp; (tab = seg.table) != <span class="keyword">null</span>) &#123;</span><br><span class="line">                      <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span> ; i &lt; tab.length; i++) &#123;</span><br><span class="line">                          HashEntry&lt;K,V&gt; e;</span><br><span class="line">                          <span class="keyword">for</span> (e = entryAt(tab, i); e != <span class="keyword">null</span>; e = e.next) &#123;</span><br><span class="line">                              V v = e.value;</span><br><span class="line">                              <span class="keyword">if</span> (v != <span class="keyword">null</span> &amp;&amp; value.equals(v)) &#123;</span><br><span class="line">                                  found = <span class="keyword">true</span>;</span><br><span class="line">                                  <span class="keyword">break</span> outer;</span><br><span class="line">                              &#125;</span><br><span class="line">                          &#125;</span><br><span class="line">                      &#125;</span><br><span class="line">                      sum += seg.modCount;</span><br><span class="line">                  &#125;</span><br><span class="line">              &#125;</span><br><span class="line">              <span class="keyword">if</span> (retries &gt; <span class="number">0</span> &amp;&amp; sum == last)</span><br><span class="line">                  <span class="keyword">break</span>;</span><br><span class="line">              last = sum;</span><br><span class="line">          &#125;</span><br><span class="line">      &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">          <span class="keyword">if</span> (retries &gt; RETRIES_BEFORE_LOCK) &#123;</span><br><span class="line">              <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; segments.length; ++j)</span><br><span class="line">                  segmentAt(segments, j).unlock();</span><br><span class="line">          &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> found;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<h3 id="2-3-5-public-Set-lt-Map-Entry-lt-K-V-gt-gt-entrySet-遍历元素方法。"><a href="#2-3-5-public-Set-lt-Map-Entry-lt-K-V-gt-gt-entrySet-遍历元素方法。" class="headerlink" title="2.3.5 public Set&lt;Map.Entry&lt;K,V&gt;&gt; entrySet(); 遍历元素方法。"></a>2.3.5 public Set&lt;Map.Entry&lt;K,V&gt;&gt; entrySet(); 遍历元素方法。</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Set&lt;Map.Entry&lt;K,V&gt;&gt; entrySet() &#123;</span><br><span class="line">        Set&lt;Map.Entry&lt;K,V&gt;&gt; es = entrySet;</span><br><span class="line">        <span class="keyword">return</span> (es != <span class="keyword">null</span>) ? es : (entrySet = <span class="keyword">new</span> EntrySet());</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">EntrySet</span> <span class="keyword">extends</span> <span class="title">AbstractSet</span>&lt;<span class="title">Map</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt;&gt; </span>&#123;</span><br><span class="line">        <span class="keyword">public</span> Iterator&lt;Map.Entry&lt;K,V&gt;&gt; iterator() &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> EntryIterator();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">contains</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (!(o <span class="keyword">instanceof</span> Map.Entry))</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            Map.Entry&lt;?,?&gt; e = (Map.Entry&lt;?,?&gt;)o;</span><br><span class="line">            V v = ConcurrentHashMap.<span class="keyword">this</span>.get(e.getKey());</span><br><span class="line">            <span class="keyword">return</span> v != <span class="keyword">null</span> &amp;&amp; v.equals(e.getValue());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">remove</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (!(o <span class="keyword">instanceof</span> Map.Entry))</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            Map.Entry&lt;?,?&gt; e = (Map.Entry&lt;?,?&gt;)o;</span><br><span class="line">            <span class="keyword">return</span> ConcurrentHashMap.<span class="keyword">this</span>.remove(e.getKey(), e.getValue());</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> ConcurrentHashMap.<span class="keyword">this</span>.size();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> ConcurrentHashMap.<span class="keyword">this</span>.isEmpty();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            ConcurrentHashMap.<span class="keyword">this</span>.clear();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">EntryIterator</span></span></span><br><span class="line"><span class="class">        <span class="keyword">extends</span> <span class="title">HashIterator</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">Iterator</span>&lt;<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt;&gt;</span></span><br><span class="line"><span class="class">    </span>&#123;</span><br><span class="line">        <span class="keyword">public</span> Map.<span class="function">Entry&lt;K,V&gt; <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            HashEntry&lt;K,V&gt; e = <span class="keyword">super</span>.nextEntry();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> WriteThroughEntry(e.key, e.value);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">HashIterator</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> nextSegmentIndex;</span><br><span class="line">        <span class="keyword">int</span> nextTableIndex;</span><br><span class="line">        HashEntry&lt;K,V&gt;[] currentTable;</span><br><span class="line">        HashEntry&lt;K, V&gt; nextEntry;</span><br><span class="line">        HashEntry&lt;K, V&gt; lastReturned;</span><br><span class="line"></span><br><span class="line">        HashIterator() &#123;</span><br><span class="line">            nextSegmentIndex = segments.length - <span class="number">1</span>;</span><br><span class="line">            nextTableIndex = -<span class="number">1</span>;</span><br><span class="line">            advance();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Set nextEntry to first node of next non-empty table</span></span><br><span class="line"><span class="comment">         * (in backwards order, to simplify checks).</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">advance</span><span class="params">()</span> </span>&#123;       <span class="comment">// @1</span></span><br><span class="line">            <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">                <span class="keyword">if</span> (nextTableIndex &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> ((nextEntry = entryAt(currentTable,</span><br><span class="line">                                             nextTableIndex--)) != <span class="keyword">null</span>)</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (nextSegmentIndex &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    Segment&lt;K,V&gt; seg = segmentAt(segments, nextSegmentIndex--);</span><br><span class="line">                    <span class="keyword">if</span> (seg != <span class="keyword">null</span> &amp;&amp; (currentTable = seg.table) != <span class="keyword">null</span>)</span><br><span class="line">                        nextTableIndex = currentTable.length - <span class="number">1</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">final</span> HashEntry&lt;K,V&gt; <span class="title">nextEntry</span><span class="params">()</span> </span>&#123;     <span class="comment">// @2</span></span><br><span class="line">            HashEntry&lt;K,V&gt; e = nextEntry;</span><br><span class="line">            <span class="keyword">if</span> (e == <span class="keyword">null</span>)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException();</span><br><span class="line">            lastReturned = e; <span class="comment">// cannot assign until after null check</span></span><br><span class="line">            <span class="keyword">if</span> ((nextEntry = e.next) == <span class="keyword">null</span>)</span><br><span class="line">                advance();</span><br><span class="line">            <span class="keyword">return</span> e;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> nextEntry != <span class="keyword">null</span>; &#125;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">hasMoreElements</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> nextEntry != <span class="keyword">null</span>; &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (lastReturned == <span class="keyword">null</span>)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException();</span><br><span class="line">            ConcurrentHashMap.<span class="keyword">this</span>.remove(lastReturned.key);</span><br><span class="line">            lastReturned = <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">WriteThroughEntry</span></span></span><br><span class="line"><span class="class">        <span class="keyword">extends</span> <span class="title">AbstractMap</span>.<span class="title">SimpleEntry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt;</span></span><br><span class="line"><span class="class">    </span>&#123;</span><br><span class="line">        WriteThroughEntry(K k, V v) &#123;</span><br><span class="line">            <span class="keyword">super</span>(k,v);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Set our entry&#x27;s value and write through to the map. The</span></span><br><span class="line"><span class="comment">         * value to return is somewhat arbitrary here. Since a</span></span><br><span class="line"><span class="comment">         * WriteThroughEntry does not necessarily track asynchronous</span></span><br><span class="line"><span class="comment">         * changes, the most recent &quot;previous&quot; value could be</span></span><br><span class="line"><span class="comment">         * different from what we return (or could even have been</span></span><br><span class="line"><span class="comment">         * removed in which case the put will re-establish). We do not</span></span><br><span class="line"><span class="comment">         * and cannot guarantee more.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> V <span class="title">setValue</span><span class="params">(V value)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (value == <span class="keyword">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">            V v = <span class="keyword">super</span>.setValue(value);</span><br><span class="line">            ConcurrentHashMap.<span class="keyword">this</span>.put(getKey(), value);</span><br><span class="line">            <span class="keyword">return</span> v;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>上述代码主要关注两点：第一是遍历元素的方法；第二是调用该迭代器的size,isEmpty等方法。</p>
<p>代码@1，advance，该方法主要是从segments[]数组中的最后一个元素开始，找出segment中HashEntity[] table数组中最后一个元素开始遍历，找到一个不为空nextEntity,这里返回的nextEntity,就是 table[]数组中的元素，不包括链表中的HashEntity，链表中的HashEntity遍历在代码@2 nextEntry 方法中，实现真的挺优雅的。</p>
<p>第二个特点是，调用迭代器的size、contains，isEmpty等方法，都是对ConcurrentHashMap对应方法的调用。</p>
<p>最后，通过上述的分析，我想对ConcurrentHashMap做一个简单的总结：</p>
<ol>
<li><p>结合上述源码分析，我们可以清楚的认为，一个Segment就是一个与HashMap相同的结构，当然每个Segment就是一把锁，该类的核心思想，就是通过对key的第一次hash,定位的不是以前的HashEntity,而是一个Segemnt,然后对该key限定在该Segment中执行，，这样可以同时允许多个线程向ConcurrentHashMap同时添加元素（当然，要分散到不同的Segment类，故提供了并发度。）</p>
</li>
<li><p>ConcurrentHashMap是一个并发容器，所谓的并发容器并不是说在使用过程中一定不需要加锁，并发容器能提    供的保证是多个线程同时访问该容器，同时调用会改变内部结构的方法时，比如put方法时，不会破坏内部结构    以至于不能提供服务或提供错误服务（数据视图）。怎么理解并不是一定不要加锁这句话，我举一个例子，比如我们用ConcurrentHashMap来存储数据，完成如下操作1、第一步，设置一个key 为”status”:1,然后经过复杂的逻辑处理，由存入一个金额 key为amount,值为10； 伪代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">ConcurrentHashMap a &#x3D; new ConcurrentHashMap();</span><br><span class="line">   </span><br><span class="line">     逻辑代码</span><br><span class="line">   </span><br><span class="line">     void eval(参数 ...) &#123;</span><br><span class="line">   </span><br><span class="line">           a.put(&quot;status&quot;,1);</span><br><span class="line">   </span><br><span class="line">           &#x2F;&#x2F;经过计算</span><br><span class="line">   </span><br><span class="line">           a.put(&quot;amount&quot;: 10);</span><br><span class="line">   </span><br><span class="line">          &#x2F;&#x2F;付款</span><br><span class="line">   </span><br><span class="line">         if(  a.get(&quot;status&quot;)&#x3D;&#x3D;1  ) &#123;</span><br><span class="line">   </span><br><span class="line">          &#x2F;&#x2F;执行付款操作</span><br><span class="line">   </span><br><span class="line">         &#125; </span><br><span class="line">   </span><br><span class="line">     &#125;  </span><br></pre></td></tr></table></figure>

<p>从上面的代码，如果多个线程执行eval方法，肯定会有问题。所以，还是需要加锁，说白了，ConcurrentHashMap只对单个方法负责，比如对 put 方法负责，只是对调用一次put方法，保证该操作，不会受到其他线程的影响。</p>
</li>
<li><p>ConcurrentHash采取如下方法从Segment[] segments,HashEntity[]  table,数组中获取元素，其准确实施性依次增强。</p>
</li>
</ol>
<ul>
<li>segment[下标],table[下标]</li>
<li>使用UNSAFE根据便宜量直接操作内存方式，使用(voliate方式)</li>
<li>重试一定次数后，加锁。</li>
</ul>
</div>

			<script src="https://my.openwrite.cn/js/readmore.js" type="text/javascript"></script>
			<script>
			var isMobile = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i);
			if (!isMobile) {
			    var btw = new BTWPlugin();
			    btw.init({
			        "id": "vip-container",
			        "blogId": "18019-1573088808868-542",
			        "name": "中间件兴趣圈",
			        "qrcode": "https://img-blog.csdnimg.cn/20190314214003962.jpg",
			        "keyword": "more"
			    });
			}
			</script>
		
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/posts/c82e1c77.html" rel="next" title="java并发锁ReentrantLock源码分析二之Condition实现原理">
                <i class="fa fa-chevron-left"></i> java并发锁ReentrantLock源码分析二之Condition实现原理
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/posts/6bc72b05.html" rel="prev" title="Dubbo服务治理之灰度发布方案（版本发布控制影响范围）">
                Dubbo服务治理之灰度发布方案（版本发布控制影响范围） <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name"></p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">139</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">18</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            

          </nav>

          

          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#1%E3%80%81ConcurrentHashMap-%E4%B8%8E-HashTable"><span class="nav-number">1.</span> <span class="nav-text">1、ConcurrentHashMap 与 HashTable</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#2%E3%80%81ConcurrentHashMap-%E8%AF%A6%E8%A7%A3"><span class="nav-number">2.</span> <span class="nav-text">2、ConcurrentHashMap 详解</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#2-1-ConcurrentHashMap%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%88%86%E6%9E%90"><span class="nav-number">2.1.</span> <span class="nav-text">2.1 ConcurrentHashMap构造方法与数据结构分析</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-2-public-V-put-K-key-V-value-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90"><span class="nav-number">2.2.</span> <span class="nav-text">2.2  public V put(K key, V value) 源码分析</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-2-public-V-putIfAbsent-K-key-V-value"><span class="nav-number">2.2.1.</span> <span class="nav-text">2.2.2 public V putIfAbsent(K key, V value)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-3-public-void-putAll-Map-m"><span class="nav-number">2.2.2.</span> <span class="nav-text">2.2.3 public void putAll(Map m)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-4-public-V-get-Object-key-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90"><span class="nav-number">2.2.3.</span> <span class="nav-text">2.2.4 public V get(Object key)源码分析</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-1-public-int-size%E6%96%B9%E6%B3%95"><span class="nav-number">2.2.4.</span> <span class="nav-text">2.3.1 public int size方法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-2-public-boolean-isEmpty-%E6%96%B9%E6%B3%95%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB"><span class="nav-number">2.2.5.</span> <span class="nav-text">2.3.2 public boolean isEmpty() 方法源码解读</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-3-public-boolean-containsKey-Object-key"><span class="nav-number">2.2.6.</span> <span class="nav-text">2.3.3 public boolean containsKey(Object key)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-4-public-boolean-containsValue-Object-value"><span class="nav-number">2.2.7.</span> <span class="nav-text">2.3.4 public boolean containsValue(Object value)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-5-public-Set-lt-Map-Entry-lt-K-V-gt-gt-entrySet-%E9%81%8D%E5%8E%86%E5%85%83%E7%B4%A0%E6%96%B9%E6%B3%95%E3%80%82"><span class="nav-number">2.2.8.</span> <span class="nav-text">2.3.5 public Set&lt;Map.Entry&lt;K,V&gt;&gt; entrySet(); 遍历元素方法。</span></a></li></ol></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">中间件兴趣圈</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Muse</a> v5.1.4</div>




        
<div class="busuanzi-count">
  <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  
    <span class="site-uv">
      <i class="fa fa-user"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
      
    </span>
  

  
    <span class="site-pv">
      <i class="fa fa-eye"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
      
    </span>
  
</div>








        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  

  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  
  <script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.4.js"></script>
  <script>AV.initialize("NNEhOL0iOcflg8f1U3HUqiCq-gzGzoHsz", "7kSmkbbb3DktmHALlShDsBUF");</script>
  <script>
    function showTime(Counter) {
      var query = new AV.Query(Counter);
      var entries = [];
      var $visitors = $(".leancloud_visitors");

      $visitors.each(function () {
        entries.push( $(this).attr("id").trim() );
      });

      query.containedIn('url', entries);
      query.find()
        .done(function (results) {
          var COUNT_CONTAINER_REF = '.leancloud-visitors-count';

          if (results.length === 0) {
            $visitors.find(COUNT_CONTAINER_REF).text(0);
            return;
          }

          for (var i = 0; i < results.length; i++) {
            var item = results[i];
            var url = item.get('url');
            var time = item.get('time');
            var element = document.getElementById(url);

            $(element).find(COUNT_CONTAINER_REF).text(time);
          }
          for(var i = 0; i < entries.length; i++) {
            var url = entries[i];
            var element = document.getElementById(url);
            var countSpan = $(element).find(COUNT_CONTAINER_REF);
            if( countSpan.text() == '') {
              countSpan.text(0);
            }
          }
        })
        .fail(function (object, error) {
          console.log("Error: " + error.code + " " + error.message);
        });
    }

    function addCount(Counter) {
      var $visitors = $(".leancloud_visitors");
      var url = $visitors.attr('id').trim();
      var title = $visitors.attr('data-flag-title').trim();
      var query = new AV.Query(Counter);

      query.equalTo("url", url);
      query.find({
        success: function(results) {
          if (results.length > 0) {
            var counter = results[0];
            counter.fetchWhenSave(true);
            counter.increment("time");
            counter.save(null, {
              success: function(counter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(counter.get('time'));
              },
              error: function(counter, error) {
                console.log('Failed to save Visitor num, with error message: ' + error.message);
              }
            });
          } else {
            var newcounter = new Counter();
            /* Set ACL */
            var acl = new AV.ACL();
            acl.setPublicReadAccess(true);
            acl.setPublicWriteAccess(true);
            newcounter.setACL(acl);
            /* End Set ACL */
            newcounter.set("title", title);
            newcounter.set("url", url);
            newcounter.set("time", 1);
            newcounter.save(null, {
              success: function(newcounter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(newcounter.get('time'));
              },
              error: function(newcounter, error) {
                console.log('Failed to create');
              }
            });
          }
        },
        error: function(error) {
          console.log('Error:' + error.code + " " + error.message);
        }
      });
    }

    $(function() {
      var Counter = AV.Object.extend("Counter");
      if ($('.leancloud_visitors').length == 1) {
        addCount(Counter);
      } else if ($('.post-title-link').length > 1) {
        showTime(Counter);
      }
    });
  </script>



  

  

  
  

  

  

  

</body>
</html>
